package org.fxml.loaders {
	import org.fxml.commands.Command;
	import org.fxml.constants.AssetTypes;
	import org.fxml.managers.AssetManager;
	import org.fxml.managers.CommandManager;

	import flash.display.Loader;
	import flash.display.LoaderInfo;
	import flash.events.ErrorEvent;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IEventDispatcher;
	import flash.events.ProgressEvent;
	import flash.net.URLLoader;
	import flash.net.URLRequest;
	import flash.system.ApplicationDomain;
	import flash.system.LoaderContext;
	import flash.text.StyleSheet;
	import flash.utils.Dictionary;
	import flash.utils.Proxy;
	import flash.utils.flash_proxy;

	/**
	 * @author jordandoczy
	 */
	public class BulkLoader extends Proxy implements IEventDispatcher {
		
		protected var _assetManager:AssetManager = new AssetManager();
		protected var _bytesLoaded:uint = 0;
		protected var _commandManager:CommandManager = new CommandManager();
		protected var _dictionary:Dictionary = new Dictionary();
		protected var _dispatcher:IEventDispatcher = new EventDispatcher(); 
		protected var _items:Array = new Array();
		
		public function BulkLoader(assetManager:AssetManager=null){
			_assetManager = assetManager || new AssetManager();
			_commandManager.addEventListener(Event.COMPLETE, onCommandManagerComplete, false, 0, true);
			_commandManager.isSequential = false;
		}
		
		override flash_proxy function callProperty(methodName:*, ... args):* {
			return this[methodName].apply(this, args);
	    }
		
		override flash_proxy function getProperty(name:*):*{
			return _assetManager.getAsset(name) || null;
		}
		
		override flash_proxy function hasProperty(name:*):Boolean{
			return _assetManager.contains(name);
		}
		
		override flash_proxy function setProperty(name:*, value:*):void{
			_assetManager.addItem(name, value);	
		}
		
		// IEventDispatcher
		public function addEventListener(type:String, listener:Function, useCapture:Boolean=false, priority:int=0, useWeakReference:Boolean=false):void{
			_dispatcher.addEventListener(type, listener, useCapture, priority, useWeakReference);
		}
		
		public function dispatchEvent(event:Event):Boolean{
			return _dispatcher.dispatchEvent(event);
		}
		
		public function hasEventListener(type:String):Boolean{
			return _dispatcher.hasEventListener(type);
		}
		
		public function removeEventListener(type:String, listener:Function, useCapture:Boolean=false):void{
			_dispatcher.removeEventListener(type, listener, useCapture);
		}
		
		public function willTrigger(type:String):Boolean{
			return _dispatcher.willTrigger(type);
		}
		
		public function get bytesLoaded():uint{
			return _bytesLoaded;
		}
		
		public function get hasBytesTotal():Boolean{
			return bytesTotal > 0;
		}
		
		public function get bytesTotal():int{
			return _items.length;
		}
		
		public function get isLoading():Boolean{
			return _commandManager.isWorking;
		}
		
		public function get isSequential():Boolean{
			return _commandManager.isSequential = false;
		}
		
		public function set isSequential(value:Boolean):void{
			_commandManager.isSequential = value;
		}
				
		public function load():void{
			_commandManager.trigger();
		}
		
		public function addCSS(path:String, key:String=null):URLLoader{
			key = key || path;
			return addItem(path, key, AssetTypes.CSS);		
		}
		
		public function addImage(path:String, key:String=null):Loader{
			key = key || path;
			return addItem(path, key, AssetTypes.IMG);
		}
		
		public function addSWF(path:String, key:String=null, context:LoaderContext=null):Loader{
			key = key || path;
			context = context || new LoaderContext(true, ApplicationDomain.currentDomain);
			return addItem(path, key, AssetTypes.SWF, context);
		}
		
		public function addXML(path:String, key:String=null):URLLoader{
			key = key || path;
			return addItem(path, key, AssetTypes.XML);
		}
		
		public function addItem(path:String, key:String, type:String, context:LoaderContext=null):*{
			
			var request:URLRequest = new URLRequest(path);
			var params:Array;
			var loader:*;

			switch(type){
				case AssetTypes.CSS:
				case AssetTypes.XML:
					loader = createURLLoader();
				break;	
				case AssetTypes.IMG:
				case AssetTypes.SWF:
					loader = createLoader();
				break;	
			}
			
			_dictionary[loader] = new LoadObject(loader, key, context, type);
			_items.push(loader);
			return loader;
			
			function createURLLoader():URLLoader{
				params = [request];
				var loader:URLLoader = new URLLoader();
					loader.addEventListener(Event.COMPLETE, onAssetLoaded, false, 0, true);
				_commandManager.addCommand(new Command(loader.load, loader, Event.COMPLETE, params));
				return loader; 	
			}
			
			function createLoader():Loader{
				params = [request, context];
				var loader:Loader = new Loader();
					loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onAssetLoaded, false, 0, true);
				_commandManager.addCommand(new Command(loader.load, loader.contentLoaderInfo, Event.COMPLETE, params));
				return loader;		
			}
		}
		
		public function getAsset(key:String):*{
			return _assetManager.getAsset(key);
		}
		
		protected function onAssetLoaded(event:Event):void{
			var loader:*;
			var loadObject:LoadObject;
			var result:*;
			
			if(event.target is LoaderInfo){
				loader = Loader(event.target.loader);
				loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, onAssetLoaded);
				loadObject = _dictionary[loader];
				result = loader.content;
			}
			else{
				loader = URLLoader(event.target);
				loader.removeEventListener(Event.COMPLETE, onAssetLoaded);
				
				loadObject = _dictionary[loader];
				
				if(loadObject.type == AssetTypes.CSS){
					var styleSheet:StyleSheet = new StyleSheet();
					styleSheet.parseCSS(loader.data);
					result = styleSheet;
				}
				else if(loadObject.type == AssetTypes.XML){
					result = new XML(loader.data);
				}
				else{
					result = loader.data;
				}
			}
			
			_assetManager.addItem(loadObject.key, result);
			_bytesLoaded++;
			dispatchEvent(new ProgressEvent(ProgressEvent.PROGRESS, true, false, bytesLoaded, bytesTotal));
		}
		
		protected function onCommandManagerComplete(event:Event):void{
			dispatchEvent(new Event(Event.COMPLETE));
		}

		protected function onError(event:ErrorEvent):void{
			dispatchEvent(event);
		}
	}
}

import flash.system.LoaderContext;

class LoadObject{
	public var loader:*;
	public var key:String;
	public var context:LoaderContext;
	public var type:String;
	
	public function LoadObject(loader:*, key:String, context:LoaderContext, type:String){
		this.loader = loader;
		this.key = key;
		this.context = context;
		this.type = type;
	}
}
